/* Declarations for the floating point types.

   This file is part of khipu.

   khipu is free software: you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */

#ifndef __KP_FLOAT__
#define __KP_FLOAT__   1

#include <cfloat>
#include "defs.hpp"
#include "arith.hpp"
#include "initop.hpp"

KP_DECLS_BEGIN

struct fltobj : public varobj
{
  static const int code = typecode::FLOAT;
  double val;

  static object make (interpreter *interp, double val);
};

#ifdef KP_ARCH_WIDE

inline constexpr bool float_p (object obj)
{
  return (itype (obj) == typecode::FLOAT);
}

#else

inline bool float_p (object obj)
{
  return (varobj_p (obj) && as_varobj(obj)->vo_type == typecode::FLOAT);
}

#endif

inline fltobj* as_fltobj (object obj)
{
  return ((fltobj *)unmask (obj));
}

#ifndef KP_ARCH_WIDE

KP_EXPORT double as_float (object obj);

#else

inline double as_float (object obj)
{
  double ret = as_fltobj(obj)->val;
  return ((obj & SIGN_BIT) ? -ret : ret);
}

#endif

const int LNDBL_SIZE = ((DBL_MANT_DIG + LIMB_BITS - 2) / LIMB_BITS + 1);

struct bigfloat : public varobj
{
  static const int code = typecode::BIGFLOAT;

  int len;
  int expo;
  limb_t *data;

  static bigfloat* alloc_raw (int len);

  void local_init (limb_t *data = nullptr, int len = 0, int expo = 0)
    {
      this->data = data;
      this->len = len;
      this->expo = expo;
    }
};

#ifdef KP_ARCH_WIDE

inline constexpr bool bigfloat_p (object obj)
{
  return (itype (obj) == typecode::BIGFLOAT);
}

#else

inline bool bigfloat_p (object obj)
{
  return (varobj_p (obj) && as_varobj(obj)->vo_type == typecode::BIGFLOAT);
}

#endif

inline bigfloat* as_bigfloat (object obj)
{
  return ((bigfloat *)unmask (obj));
}

// Test if DBL is NaN.
KP_EXPORT bool fnan_p (double dbl);

// Test if DBL is +/- infinity.
KP_EXPORT int finf_p (double dbl);

// Allocate a bigfloat with LEN limbs.
KP_EXPORT object alloc_bigfloat (interpreter *interp, int len);

// Arithmetic functions with floats.
KP_EXPORT object add_ff (interpreter *interp, object x, object y);
KP_EXPORT object add_fF (interpreter *interp, object x, object y);
KP_EXPORT object add_FF (interpreter *interp, object x, object y);

KP_EXPORT object sub_ff (interpreter *interp, object x, object y);
KP_EXPORT object sub_fF (interpreter *interp, object x, object y);
KP_EXPORT object sub_FF (interpreter *interp, object x, object y);

KP_EXPORT object mul_ff (interpreter *interp, object x, object y);
KP_EXPORT object mul_fF (interpreter *interp, object x, object y);
KP_EXPORT object mul_FF (interpreter *interp, object x, object y);

KP_EXPORT object div_ff (interpreter *interp, object x, object y);
KP_EXPORT object div_fF (interpreter *interp, object x, object y);
KP_EXPORT object div_Ff (interpreter *interp, object x, object y);
KP_EXPORT object div_FF (interpreter *interp, object x, object y);

KP_EXPORT object mod_ff (interpreter *interp, object x, object y);
KP_EXPORT object mod_fF (interpreter *interp, object x, object y);
KP_EXPORT object mod_Ff (interpreter *interp, object x, object y);
KP_EXPORT object mod_FF (interpreter *interp, object x, object y);

// Comparisons between floating-point objects.
KP_EXPORT int cmp_ff (interpreter *interp, object x, object y);
KP_EXPORT int cmp_fF (interpreter *interp, object x, object y);
KP_EXPORT int cmp_FF (interpreter *interp, object x, object y);
KP_EXPORT bool eq_ff (interpreter *interp, object x, object y);
KP_EXPORT bool eq_FF (interpreter *interp, object x, object y);

// Mixed arithmetic functions.

#define MIXED_FN(name)   \
KP_EXPORT object name##_if (interpreter *interp, object x, object y);   \
KP_EXPORT object name##_fi (interpreter *interp, object x, object y);   \
KP_EXPORT object name##_iF (interpreter *interp, object x, object y);   \
KP_EXPORT object name##_Fi (interpreter *interp, object x, object y);   \
\
KP_EXPORT object name##_If (interpreter *interp, object x, object y);   \
KP_EXPORT object name##_fI (interpreter *interp, object x, object y);   \
KP_EXPORT object name##_IF (interpreter *interp, object x, object y);   \
KP_EXPORT object name##_FI (interpreter *interp, object x, object y)

MIXED_FN (add);
MIXED_FN (sub);
MIXED_FN (mul);
MIXED_FN (div);
MIXED_FN (mod);

#undef MIXED_FN

KP_EXPORT object lsh_fi (interpreter *interp, object x, object y);
KP_EXPORT object lsh_Fi (interpreter *interp, object x, object y);

KP_EXPORT object rsh_fi (interpreter *interp, object x, object y);
KP_EXPORT object rsh_Fi (interpreter *interp, object x, object y);

KP_EXPORT int cmp_if (interpreter *interp, object x, object y);
KP_EXPORT int cmp_iF (interpreter *interp, object x, object y);
KP_EXPORT int cmp_If (interpreter *interp, object x, object y);
KP_EXPORT int cmp_IF (interpreter *interp, object x, object y);

KP_EXPORT bool eq_if (interpreter *interp, object x, object y);
KP_EXPORT bool eq_If (interpreter *interp, object x, object y);
KP_EXPORT bool eq_IF (interpreter *interp, object x, object y);

// Additional unary functions.

KP_EXPORT object neg_f (interpreter *interp, object obj);
KP_EXPORT object neg_F (interpreter *interp, object obj);
KP_EXPORT uint32_t hash_f (interpreter *interp, object obj);
KP_EXPORT uint32_t hash_F (interpreter *interp, object obj);

// I/O with floats.

struct stream;
struct io_info;
struct pack_info;

KP_EXPORT int write_f (interpreter *interp,
  stream *strm, object obj, io_info& info);

KP_EXPORT int write_F (interpreter *interp,
  stream *strm, object obj, io_info& info);

KP_EXPORT int64_t pack_f (interpreter *interp,
  stream *strm, object obj, pack_info& info);

KP_EXPORT int pack_F (interpreter *interp,
  stream *strm, object obj, pack_info& info);

KP_EXPORT object unpack_f (interpreter *interp,
  stream *strm, pack_info& info, bool save);

KP_EXPORT object unpack_F (interpreter *interp,
  stream *strm, pack_info& info, bool save);

// Global objects.
KP_EXPORT object FLT_PINF;   // +INF
KP_EXPORT object FLT_NINF;   // -INF
KP_EXPORT object FLT_QNAN;   // QNaN
KP_EXPORT object FLT_ZERO;   // 0.0

// Init OP for floats.
KP_EXPORT init_op init_float;

KP_DECLS_END

#endif
